package student.web.internal;

import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import org.zkoss.util.logging.Log;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.CheckEvent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.MouseEvent;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zk.ui.util.EventInterceptor;
import org.zkoss.zkplus.databind.DataBinder;

//-------------------------------------------------------------------------
/**
 *  Intended as an event interceptor listener for a desktop, this class
 *  will automatically reload binding values after relevant page
 *  events on any of the desktop's pages.
 *
 *  @author Stephen Edwards
 *  @author Last changed by $Author: edwards@cs.vt.edu $
 *  @version $Revision: 87 $, $Date: 2010-02-15 15:54:26 +0000 (Mon, 15 Feb 2010) $
 */
public class BindingUpdater
    implements EventInterceptor
{
    //~ Constructor ...........................................................

    // ----------------------------------------------------------
    /**
     * Creates a new event interceptor for the given desktop.
     */
    public BindingUpdater(Desktop desktop)
    {
        this.desktop = desktop;
    }


    //~ Methods ...............................................................

    // ----------------------------------------------------------
    public void afterProcessEvent(Event event)
    {
        if (log.debugable())
        {
            log.debug("afterProcessEvent(" + event + ")");
        }
        if (processing.size() > 0)
        {
            if (processing.peek() == event)
            {
                processing.pop();
            }
            else if (processing.contains(event))
            {
                while (processing.size() > 0)
                {
                    if (processing.pop() == event)
                    {
                        break;
                    }
                }
            }
        }
        if (triggeredEvents.remove(event))
        {
            log.debug("    event was triggered by binding update");
            updatingBindings = false;
            return;
        }
        if (!interestingEvent(event))
        {
            // ignore these
            log.debug("    ignoring non-interaction event");
            return;
        }

        log.debug("    starting binder loading");
        updatingBindings = true;
        for (Object pg : desktop.getPages())
        {
            Page page = (Page)pg;
            Object binder = page.getVariable("binder");
            if (binder != null)
            {
                log.debug("    loading bindings!");
                ((DataBinder)binder).loadAll();
            }
        }
        updatingBindings = false;
        log.debug("    finished binder loading");
    }


    // ----------------------------------------------------------
    private boolean interestingEvent(Event event)
    {
        return event instanceof MouseEvent
            || event instanceof SelectEvent
            || event instanceof CheckEvent;
    }


    // ----------------------------------------------------------
    public Event beforePostEvent(Event event)
    {
        if (log.debugable())
        {
            log.debug("beforePostEvent(" + event + ")");
        }
        if (updatingBindings)
        {
            if (event instanceof SelectEvent)
            {
                log.debug("    intercepting screened select");
                return null;
            }
            log.debug("    event will be screened");
            triggeredEvents.add(event);
        }
        return event;
    }


    // ----------------------------------------------------------
    public Event beforeProcessEvent(Event event)
    {
        if (log.debugable())
        {
            log.debug("beforeProcessEvent(" + event + ")");
        }
        processing.push(event);
        if (triggeredEvents.contains(event))
        {
            log.debug("    event is being screened");
            updatingBindings = true;
        }
        return event;
    }


    // ----------------------------------------------------------
    public Event beforeSendEvent(Event event)
    {
        if (log.debugable())
        {
            log.debug("beforeSendEvent(" + event + ")");
        }
        if (updatingBindings)
        {
            log.debug("    event will be screened");
            triggeredEvents.add(event);
        }
        return event;
    }


    //~ Instance/static variables .............................................

    private Desktop desktop;
    private Set<Event> triggeredEvents = new HashSet<Event>();
    private Stack<Event> processing = new Stack<Event>();
    private boolean updatingBindings = false;

    private static final Log log = Log.lookup(BindingUpdater.class);
}
